from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import render
# Create your views here.
from django.views import View
from django_redis import get_redis_connection

from users.models import Address
from goods.models import SKU
from decimal import Decimal

from meiduo_mall.utils.views import LoginRequiredJSONMixin
import json
from django import http
from .models import OrderInfo,OrderGoods
from django.utils import timezone
# 导入响应码类
from meiduo_mall.utils.response_code import RETCODE

from django.db import transaction # 导入事务模块

class OrderSuccessView(LoginRequiredMixin, View):
    """提交订单成功"""
    def get(self, request):
        order_id = request.GET.get('order_id')
        payment_amount = request.GET.get('payment_amount')
        pay_method = request.GET.get('pay_method')

        context = {
            'order_id':order_id,
            'payment_amount':payment_amount,
            'pay_method':pay_method
        }
        return render(request, 'order_success.html', context)

class OrderCommitView(LoginRequiredJSONMixin, View):
    """订单提交"""

    def post(self, request):
        """保存订单信息和订单商品信息"""
        # 获取当前要保存的订单数据
        json_dict = json.loads(request.body.decode())
        address_id = json_dict.get('address_id')
        pay_method = json_dict.get('pay_method')
        # 校验参数
        if not all([address_id, pay_method]):
            return http.HttpResponseForbidden('缺少必传参数')
        # 判断address_id是否合法
        try:
            address = Address.objects.get(id=address_id)
        except Exception:
            return http.HttpResponseForbidden('参数address_id错误')
            # 判断pay_method是否合法
        if pay_method not in [OrderInfo.PAY_METHODS_ENUM['CASH'],
                              OrderInfo.PAY_METHODS_ENUM['ALIPAY']]:
            return http.HttpResponseForbidden('参数pay_method错误')

        # 显示开启一个事务
        with transaction.atomic():
            # 在数据库操作之前需要指定保存点，保存数据库的初始状态
            save_id = transaction.savepoint()
            try:
                # 获取登录用户
                user = request.user
                # 生成订单编号：年月日时分秒+用户编号
                order_id = timezone.localtime().strftime('%Y%m%d%H%M%S') + ('%09d' % user.id)
                # 保存订单基本信息 OrderInfo（一）
                order = OrderInfo.objects.create(
                    order_id=order_id,
                    user=user,
                    address=address,
                    total_count=0,
                    total_amount=Decimal('0'),
                    freight=Decimal('10.00'),
                    pay_method=pay_method,
                    status=OrderInfo.ORDER_STATUS_ENUM['UNPAID']
                    if pay_method == OrderInfo.PAY_METHODS_ENUM['ALIPAY']
                    else OrderInfo.ORDER_STATUS_ENUM['UNSEND']
                )

                # 查询redis购物车中被勾选的商品
                redis_conn = get_redis_connection("carts")
                # 获取所有购物车数据：包括勾选的和未勾选的：{b'1':b'1',b'2':b'2'}
                redis_cart = redis_conn.hgetall("carts_%s" % user.id)
                # 获取被勾选商品的sku_id值：{b'1'}
                redis_selected = redis_conn.smembers("selected_%s" % user.id)
                # 构造购物车中被勾选的商品数据 {'b'1':b'1'}
                new_cart_dict = {}
                for sku_id in redis_selected:
                    # new_cart_dict[sku_id]=商品数量
                    new_cart_dict[int(sku_id)] = int(redis_cart[sku_id])
                # 遍历new_cart_dict,取出其中的sku_id和count
                sku_ids = new_cart_dict.keys()
                for sku_id in sku_ids:

                    # 读取购物车中商品信息
                    # 查询商品和库存信息时，不能出现缓存，所以没用filter(id__in=sku_ids)
                    # 每个商品都有多次下单机会，直到库存不足
                    while True:
                        sku = SKU.objects.get(id=sku_id)

                        # 获取原始库存和销量
                        origin_stock=sku.stock
                        origin_sales=sku.sales

                        # 获取要提交订单的商品数量
                        sku_count = new_cart_dict[sku.id]
                        # 判断商品数量是否大于库存,如果大于，则响应"库存不足"
                        if sku_count>origin_stock:
                            # 库存不足，回滚事务
                            transaction.savepoint_rollback(save_id)
                            return http.JsonResponse({"code":RETCODE.STOCKERR,"errmsg":"库存不足"})

                        # # 模拟网络延迟
                        # import time
                        # time.sleep(7)

                        # sku减库存，加销量
                        # sku.stock-=sku_count
                        # sku.sales+=sku_count
                        # sku.save()

                        new_stock=origin_stock - sku_count
                        new_sales = origin_sales+sku_count
                        result = SKU.objects.filter(id=sku_id,stock=origin_stock).update(stock=new_stock,sales=new_sales)
                        # 如果在更新数据时，原始数据变了返回0，表示有资源抢夺
                        if result==0:
                            # 库存为10，要买一个，但是在下单时有资源抢夺，被买走了一个，还要9个，如果库存依然满足，继续下单，直到库存不足为止
                            # return http.HttpResponse("下单失败")
                            continue # 有资源抢夺，则进行下一次循环
                        # spu加销量
                        sku.spu.sales+=sku_count
                        sku.spu.save()
                        # 保存订单商品信息(多)
                        OrderGoods.objects.create(
                            order=order,
                            sku = sku,
                            count = sku_count,
                            price =sku.price
                        )
                        # 累加订单商品总数量和总价格到订单基本信息表中
                        order.total_count+=sku_count
                        order.total_amount+=sku_count*sku.price
                        # 下单成功，记得退出循环
                        break
                # 最后在加上邮费
                order.total_amount+=order.freight
                order.save()
            except Exception as e:
                print("e=",e)
                # 出现异常回滚事务
                transaction.savepoint_rollback(save_id)
                return http.JsonResponse({"code":RETCODE.DBERR,"errmsg":"下单失败"})

            # 数据库操作成功，显式提交事务
            transaction.savepoint_commit(save_id)

        return http.JsonResponse({"code":RETCODE.OK,"errmsg":"OK","order_id":order_id})

class OrderSettlementView(LoginRequiredMixin, View):
    """结算订单"""
    def get(self, request):
        """查询并展示要结算的订单数据"""
        # 获取登录用户
        user = request.user
        try:
            # 查询用户收货地址：查询登录用户没有被删除的收货地址
            addresses = Address.objects.filter(user=user,is_deleted=False)
        except Exception as e:
            # 如果没有查询出地址，可以编辑收货地址
            addresses = None
        # 查询redis购物车中被勾选的商品
        redis_conn = get_redis_connection("carts")
        # 获取所有购物车数据：包括勾选的和未勾选的：{b'1':b'1',b'2':b'2'}
        redis_cart=redis_conn.hgetall("carts_%s"%user.id)
        # 获取被勾选商品的sku_id值：{b'1'}
        redis_selected = redis_conn.smembers("selected_%s"%user.id)
        # 构造购物车中被勾选的商品数据 {'b'1':b'1'}
        new_cart_dict = {}
        for sku_id in redis_selected:
            # new_cart_dict[sku_id]=商品数量
            new_cart_dict[int(sku_id)]=int(redis_cart[sku_id])
        # 遍历new_cart_dict,取出其中的sku_id和count
        sku_ids = new_cart_dict.keys()
        skus = SKU.objects.filter(id__in=sku_ids)

        total_count=0 # 商品总数量
        total_amount=Decimal(0.00) # 商品总金额
        # 遍历skus给每个sku对象补充count(数量)和amount(小计)
        # 取出所有sku,动态绑定商品数量和小计
        for sku in skus:
            sku.count=new_cart_dict[sku.id]
            sku.amount=sku.price*sku.count
            # 累加数量和金额
            total_count+=sku.count
            total_amount+=sku.amount
        # 指定默认的邮费
        freight=Decimal(10.00)
        # 构造上下文
        context = {
            'addresses':addresses,
            'skus':skus,
            'total_count':total_count, # 商品总数量
            'total_amount':total_amount, # 总金额
            'freight':freight, # 邮费
            'payment_amount':total_amount+freight # 实付款
        }
        return render(request, 'place_order.html',context=context)